/*
 * Copyright (c) 2003, the JUNG Project and the Regents of the University of
 * California All rights reserved.
 * 
 * This software is open-source under the BSD license; see either "license.txt"
 * or http://jung.sourceforge.net/license.txt for a description.
 * 
 */


import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Label;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;

import com.facebook.api.*;
import com.facebook.api.schema.User;
import com.facebook.api.schema.UsersGetInfoResponse;
import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiNilLoader.Array;

import edu.uci.ics.jung.graph.ArchetypeVertex;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.Vertex;
import edu.uci.ics.jung.graph.decorators.DefaultToolTipFunction;
import edu.uci.ics.jung.graph.decorators.DefaultVertexIconFunction;
import edu.uci.ics.jung.graph.decorators.EdgeShape;
import edu.uci.ics.jung.graph.decorators.EllipseVertexShapeFunction;
import edu.uci.ics.jung.graph.decorators.PickableEdgePaintFunction;
import edu.uci.ics.jung.graph.decorators.PickableVertexPaintFunction;
import edu.uci.ics.jung.graph.decorators.VertexIconAndShapeFunction;
import edu.uci.ics.jung.graph.decorators.VertexPaintFunction;
import edu.uci.ics.jung.graph.decorators.VertexShapeFunction;
import edu.uci.ics.jung.graph.decorators.VertexStringer;
import edu.uci.ics.jung.graph.impl.UndirectedSparseEdge;
import edu.uci.ics.jung.graph.impl.UndirectedSparseGraph;
import edu.uci.ics.jung.graph.impl.UndirectedSparseVertex;
import edu.uci.ics.jung.utils.UserData;
import edu.uci.ics.jung.visualization.DefaultGraphLabelRenderer;
import edu.uci.ics.jung.visualization.FRLayout;
import edu.uci.ics.jung.visualization.FourPassImageShaper;
import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
import edu.uci.ics.jung.visualization.PickedState;
import edu.uci.ics.jung.visualization.PluggableRenderer;
import edu.uci.ics.jung.visualization.ShapePickSupport;
import edu.uci.ics.jung.visualization.SpringLayout;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.contrib.CircleLayout;
import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
import edu.uci.ics.jung.visualization.control.ScalingControl;

/**
 * Demonstrates the use of images to represent graph vertices.
 * The images are supplied via the VertexShapeFunction so that
 * both the image and its shape can be utilized.
 * 
 * The images used in this demo (courtesy of slashdot.org) are
 * rectangular but with a transparent background. When vertices
 * are represented by these images, it looks better if the actual
 * shape of the opaque part of the image is computed so that the
 * edge arrowheads follow the visual shape of the image. This demo
 * uses the FourPassImageShaper class to compute the Shape from
 * an image with transparent background.
 * 
 * @author Tom Nelson - RABA Technologies
 * 
 */
public class VertexImageShaper extends JApplet {

	/**
	 * the graph
	 */
	Graph graph;

	/**
	 * the visual component and renderer for the graph
	 */
	VisualizationViewer vv;

//	private ArrayList<UndirectedSparseVertex> tab_vertex;

//	private UndirectedSparseVertex racine;

	private ArrayList<UndirectedSparseEdge> tab_edge;
//	private ArrayList<UndirectedSparseEdge> tab_edge_relation;
	private HashMap<Long, UndirectedSparseVertex> tab_vertex;
	private HashMap<UndirectedSparseVertex, String> map_name;
	private HashMap<UndirectedSparseVertex, Icon> map_icon;

	private FBManagement fbm;

	public Vertex v_select;

	public Long id_user_select;


	/**
	 * create an instance of a simple graph with controls to
	 * demo the zoom features.
	 * 
	 */
	public VertexImageShaper(final FBManagement fbm) {
		this.fbm = fbm ;
		graph = new UndirectedSparseGraph();
		tab_edge = new ArrayList<UndirectedSparseEdge>() ;
//		tab_edge_relation = new ArrayList<UndirectedSparseEdge>() ;
		tab_vertex = new HashMap<Long, UndirectedSparseVertex>() ;

		UndirectedSparseVertex racine = addVertices(fbm.myID);

		map_name = new HashMap<UndirectedSparseVertex, String>();
		map_icon = new HashMap<UndirectedSparseVertex, Icon>();
		setVertexInfo() ;


//		Init du vecteur tab_edge :
		createEdges(racine);
		createRelation();


		// This demo uses a special renderer to turn outlines on and off.
		// you do not need to do this in a real application. Just use a PluggableRender
		final PluggableRenderer pr = new DemoRenderer();
		final VertexStringerImpl vertexStringerImpl = 
			new VertexStringerImpl(map_name);
		pr.setVertexStringer(vertexStringerImpl);
		VertexPaintFunction vpf = new PickableVertexPaintFunction(pr, Color.BLUE, Color.white, Color.yellow);
		pr.setVertexPaintFunction(vpf);
		pr.setEdgePaintFunction(new PickableEdgePaintFunction(pr, Color.BLUE, Color.RED));
		pr.setGraphLabelRenderer(new DefaultGraphLabelRenderer(Color.RED, Color.RED));

		// For this demo only, I use a special class that lets me turn various
		// features on and off. For a real application, use VertexIconAndShapeFunction instead.
		final DemoVertexImageShapeFunction vertexImagerAndShapeFunction =
			new DemoVertexImageShapeFunction(new EllipseVertexShapeFunction());
		vertexImagerAndShapeFunction.setIconMap(map_icon);
		pr.setVertexShapeFunction(vertexImagerAndShapeFunction);
		pr.setVertexIconFunction(vertexImagerAndShapeFunction);

		final FRLayout layout = new FRLayout(graph);
		layout.setMaxIterations(100);
		vv =  new VisualizationViewer(layout, pr, new Dimension(800,600));
		vv.setPickSupport(new ShapePickSupport());
		pr.setEdgeShapeFunction(new EdgeShape.QuadCurve());
//		vv.setBackground(Color.white);
		vv.setBackground(new Color(218, 227, 236));



		// Get the pickedState and add a listener that will decorate the
		// Vertex images with a checkmark icon when they are picked
		PickedState ps = vv.getPickedState();
		ps.addItemListener(new PickWithIconListener(vertexImagerAndShapeFunction));

		// add a listener for ToolTips
		vv.setToolTipFunction(new DefaultToolTipFunction());

		Container content = getContentPane();
		final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv);
		content.add(panel);

		final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse();
//		final ModalGraphMouse graphMouse = new ModalGraphMouse(); 
		graphMouse.setMode(ModalGraphMouse.Mode.PICKING) ;
		vv.setGraphMouse(graphMouse);

		final ScalingControl scaler = new CrossoverScalingControl();

		JButton plus = new JButton("+");
		plus.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				scaler.scale(vv, 1.1f, vv.getCenter());
			}
		});
		JButton minus = new JButton("-");
		minus.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				scaler.scale(vv, 1/1.1f, vv.getCenter());
			}
		});

		JCheckBox shape = new JCheckBox("Shape");
		shape.addItemListener(new ItemListener(){

			public void itemStateChanged(ItemEvent e) {
				vertexImagerAndShapeFunction.setShapeImages(e.getStateChange()==ItemEvent.SELECTED);
				vv.repaint();
			}
		});
		shape.setSelected(true);

		JCheckBox fill = new JCheckBox("Fill");
		fill.addItemListener(new ItemListener(){

			public void itemStateChanged(ItemEvent e) {
				vertexImagerAndShapeFunction.setFillImages(e.getStateChange()==ItemEvent.SELECTED);
				vv.repaint();
			}
		});
		fill.setSelected(true);

		JCheckBox drawOutlines = new JCheckBox("Outline");
		drawOutlines.addItemListener(new ItemListener(){

			public void itemStateChanged(ItemEvent e) {
				vertexImagerAndShapeFunction.setOutlineImages(e.getStateChange()==ItemEvent.SELECTED);
				vv.repaint();
			}
		});

		JComboBox modeBox = graphMouse.getModeComboBox();
		JPanel modePanel = new JPanel();
		modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode"));
		modePanel.add(modeBox);

		JPanel scaleGrid = new JPanel(new GridLayout(1,0));
		scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom"));
		JPanel labelFeatures = new JPanel(new GridLayout(1,0));
		labelFeatures.setBorder(BorderFactory.createTitledBorder("Image Effects"));
		final JPanel controls = new JPanel();
		scaleGrid.add(plus);
		scaleGrid.add(minus);
		controls.add(scaleGrid);
		labelFeatures.add(shape);
		labelFeatures.add(fill);
		labelFeatures.add(drawOutlines);

		controls.add(labelFeatures);
		controls.add(modePanel);

		/* Menu Appli : 
		 * 
		 */
		JButton baffeButton = new JButton("Fous des baffes !");
		baffeButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if ( v_select != null)
				{
					JButton b = (JButton) e.getSource() ;
					b.setText("Chargement en cours !");
					b.repaint();

					Long id_user = (Long) v_select.getUserDatum("id") ;
					fbm.envoyerBaffes(id_user);
					System.out.println("Fous des baffes: " + v_select);

					b.setText("Fous des baffes !");
					b.repaint();
				}
			}
		});

		final JPanel fonctionGrid = new JPanel(new GridLayout(1,0));
		fonctionGrid.setBorder(BorderFactory.createTitledBorder("Features"));
		
		JButton amisButton = new JButton("Afficher ses amis");
		amisButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if ( v_select != null)
				{
					System.out.println("Afficher ses amis: " + v_select);
					Label l = new Label("Chargement en cours !");
					fonctionGrid.add(l);
					controls.repaint();
//					JButton b = (JButton) e.getSource() ;
//					b.setText("Chargement en cours !");
//					b.repaint();
					
					if ((Boolean)v_select.getUserDatum("isPainted") == true) return;

					Long id_user = (Long) v_select.getUserDatum("id") ;
					fbm.getUserInfos(id_user);
					UndirectedSparseVertex racine_clic = addVertices(id_user);
					setVertexInfo();
					createEdges(racine_clic);
					createRelation();

//					b.setText("Afficher ses amis");
//					b.repaint();
					layout.update();
					fonctionGrid.remove(l);
					controls.repaint();


					v_select.setUserDatum("isPainted", true, UserData.SHARED);

				}
			}
		});



		fonctionGrid.add(baffeButton);
		fonctionGrid.add(amisButton);
		controls.add(fonctionGrid);


		content.add(controls, BorderLayout.SOUTH);

////		Partie JFrame : 
		JFrame frame = new JFrame();
		Container content2 = frame.getContentPane();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		content2.add(this);
		frame.pack();
		frame.setVisible(true);
		
	} // public VertexImageShaper

	private void setVertexInfo() {
		for(Entry<Long, UndirectedSparseVertex> entry : tab_vertex.entrySet()) {
			Long cle = entry.getKey();
			UndirectedSparseVertex valeur = entry.getValue();

			if ( !map_name.containsValue(valeur) )
			{
				map_name.put(valeur, (String) valeur.getUserDatum("name" )) ;
			}

			if ( !map_icon.containsValue(valeur) )
			{
				String name = (String) valeur.getUserDatum("pic" );
				try {
					Icon icon = new LayeredIcon(new ImageIcon(new URL(name)).getImage());
					map_icon.put(valeur, icon) ;
				} catch(Exception ex) {
					System.err.println("Image non chargée " + name);
				}	
			}
		}
	} // setVertexInfo


	/**
	 * create some vertices
	 * @param ids.size() how many to create
	 * @return the Vertices in an array
	 */
//	Ajout vertex aux graphes depuis la racine (vertex de l'id_user)
	private UndirectedSparseVertex addVertices(Long id_user) {
		if ( ! tab_vertex.containsKey(id_user) )
		{
			UndirectedSparseVertex racine = new UndirectedSparseVertex() ;
			racine.addUserDatum("id", id_user, UserData.SHARED);
			racine.addUserDatum("name", fbm.tab_noms.get(id_user), UserData.SHARED);
			racine.addUserDatum("pic", fbm.tab_pic.get(id_user), UserData.SHARED);
			racine.addUserDatum("isPainted", new Boolean(true), UserData.SHARED);
			graph.addVertex(racine);
			tab_vertex.put(id_user, racine);
		}

		UndirectedSparseVertex racine = tab_vertex.get(id_user) ;

//		Ajout des amis d'id_user : 
		ArrayList<Long> id_mes_amis =  fbm.tab_id_friends.get(id_user) ;
		System.out.println("id_mes_amis : " + id_mes_amis) ;
		for (int i = 0; i < id_mes_amis.size(); i++) {
			if ( ! tab_vertex.containsKey(id_mes_amis.get(i)) )
			{
				UndirectedSparseVertex v = new UndirectedSparseVertex();
				v.addUserDatum("id", id_mes_amis.get(i), UserData.SHARED);
				String name = fbm.tab_noms.get(id_mes_amis.get(i));
				String pic = fbm.tab_pic.get(id_mes_amis.get(i));

				//v.addUserDatum("name", name, UserData.SHARED);
				if (pic != null) v.addUserDatum("pic", pic, UserData.SHARED);
				v.addUserDatum("isPainted", new Boolean(false), UserData.SHARED);

				if (name != null){
					v.addUserDatum("name", name, UserData.SHARED);
					tab_vertex.put(id_mes_amis.get(i), v) ;
					graph.addVertex(v);
				}
				//else fbm.tab_id_friends.remove(id_mes_amis.get(i));
//				2b. On ajoute au graphe

			}
		}

		System.out.println("racine : " + racine) ;
		return racine;
	} // private UndirectedSparseVertex addVertices

	/**
	 * create edges for this demo graph
	 * @param v an array of Vertices to connect
	 */
	void createEdges(UndirectedSparseVertex racine) {
		System.out.println("Liste vertices : " + graph.getVertices()) ;
		ArrayList<Long> my_friends = fbm.tab_id_friends.get(racine.getUserDatum("id")) ;
		Long id_racine = (Long) racine.getUserDatum("id");
		for (int i = 1 ; i < my_friends.size(); ++i)
		{
			if (tab_vertex.get(my_friends.get(i)) == null) continue;
			System.out.println("Couple vertex : "+ racine + " / " + tab_vertex.get( my_friends.get(i))) ;
			UndirectedSparseEdge e ;
			if ( id_racine < my_friends.get(i))
			{
				e = new UndirectedSparseEdge(racine, tab_vertex.get( my_friends.get(i)) ) ;
			}
			else
			{
				e = new UndirectedSparseEdge(tab_vertex.get( my_friends.get(i)), racine );
			}

			boolean alreadyIn = false;
			for (UndirectedSparseEdge e1 : tab_edge){			
				Set <UndirectedSparseEdge> s = (Set <UndirectedSparseEdge>)e1.getIncidentVertices();	
				if (s.contains(racine) && s.contains(tab_vertex.get(my_friends.get(i))))
				{
					alreadyIn = true;
					break;
				}
			}
//			if (!alreadyIn)
//			for (UndirectedSparseEdge e1 : tab_edge_relation){			
//			Set <UndirectedSparseEdge> s = (Set <UndirectedSparseEdge>)e1.getIncidentVertices();
//			if (s.contains(racine) && s.contains(tab_vertex.get(my_friends.get(i))))
//			{
//			alreadyIn = true;
//			break;
//			}
//			}
			if (!alreadyIn)
			{
				tab_edge.add(e);
				graph.addEdge(e);
			}
		}
	}	


//	TODO : fait à l'arrache 
	void createRelation()
	{
		for (ArrayList<Long> couple : fbm.relations_amis)
		{
			UndirectedSparseEdge edge_temp = new UndirectedSparseEdge(
					tab_vertex.get(couple.get(0)), tab_vertex.get(couple.get(1))) ;
			boolean alreadyIn = false;
			for (UndirectedSparseEdge e1 : tab_edge){			
				Set <UndirectedSparseEdge> s = (Set <UndirectedSparseEdge>)e1.getIncidentVertices();	
				if (s.contains(tab_vertex.get(couple.get(0))) && s.contains(tab_vertex.get(couple.get(1))))
				{
					alreadyIn = true;
					break;
				}
			}

			if (!alreadyIn)
			{
				tab_edge.add(edge_temp);
				graph.addEdge(edge_temp);
			}
//			System.out.println("Relou : " + couple.get(0) + "##" + couple.get(1));
//			UndirectedSparseEdge edge_temp = new UndirectedSparseEdge(
//			tab_vertex.get(couple.get(0)), tab_vertex.get(couple.get(1))) ;

//			if ( ! tab_edge.contains(edge_temp))
//			{
//			tab_edge.add(edge_temp);
//			graph.addEdge(edge_temp) ;
//			}
		}

	}



	/**
	 * When Vertices are picked, add a checkmark icon to the imager.
	 * Remove the icon when a Vertex is unpicked
	 * @author Tom Nelson - RABA Technologies
	 *
	 */
	public class PickWithIconListener implements ItemListener {
		//DefaultVertexIconFunction imager;
		//Icon checked;

		public PickWithIconListener(DefaultVertexIconFunction imager) {
			//this.imager = imager;
			//checked = new Checkmark();
		}

		public void itemStateChanged(ItemEvent e) {
			System.out.println("e: " + e.getStateChange());
			if ( e.getStateChange() == ItemEvent.SELECTED )
			{
				if(e.getItem() instanceof Vertex) {
					v_select = (Vertex)e.getItem();
					System.out.println("v: " + v_select);
					id_user_select = (Long) v_select.getUserDatum("id") ;

//					fbm.getUserInfos(id_user_select);
//					UndirectedSparseVertex racine_clic = addVertices(id_user_select);
//					createEdges(racine_clic);

					//	getFriends(v);
					/*Icon icon = imager.getIcon(v);
				if(icon instanceof LayeredIcon) {
					if(e.getStateChange() == ItemEvent.SELECTED) {
						((LayeredIcon)icon).add(checked);
					} else {
						((LayeredIcon)icon).remove(checked);
					}
				}*/
				}
			}
			else
			{
				v_select = null ; 
				System.out.println("v unselect !!");
			}
		}
	} // public class PickWithIconListener





	/**
	 * A simple implementation of VertexStringer that
	 * gets Vertex labels from a Map  
	 * 
	 * @author Tom Nelson - RABA Technologies
	 *
	 *
	 */
	public static class VertexStringerImpl implements VertexStringer {

		Map map = new HashMap();

		boolean enabled = true;

		public VertexStringerImpl(Map map) {
			this.map = map;
		}

		/* (non-Javadoc)
		 * @see edu.uci.ics.jung.graph.decorators.VertexStringer#getLabel(edu.uci.ics.jung.graph.Vertex)
		 */
		public String getLabel(ArchetypeVertex v) {
			if(isEnabled()) {
				return (String)map.get(v);
			} else {
				return "";
			}
		}

		/**
		 * @return Returns the enabled.
		 */
		public boolean isEnabled() {
			return enabled;
		}

		/**
		 * @param enabled The enabled to set.
		 */
		public void setEnabled(boolean enabled) {
			this.enabled = enabled;
		}
	}



	/** 
	 * this class exists only to provide settings to turn on/off shapes and image fill
	 * in this demo.
	 * In a real application, use VertexIconAndShapeFunction instead.
	 * 
	 */
	public static class DemoVertexImageShapeFunction extends VertexIconAndShapeFunction {

		boolean shapeImages = true;
		boolean fillImages = true;
		boolean outlineImages = false;
		public DemoVertexImageShapeFunction(VertexShapeFunction delegate) {
			super(delegate);
		}
		/**
		 * @return Returns the fillImages.
		 */
		public boolean isFillImages() {
			return fillImages;
		}
		/**
		 * @param fillImages The fillImages to set.
		 */
		public void setFillImages(boolean fillImages) {
			this.fillImages = fillImages;
		}
		/**
		 * @return Returns the shapeImages.
		 */
		public boolean isShapeImages() {
			return shapeImages;
		}
		/**
		 * @param shapeImages The shapeImages to set.
		 */
		public void setShapeImages(boolean shapeImages) {
			shapeMap.clear();
			this.shapeImages = shapeImages;
		}
		public boolean isOutlineImages() {
			return outlineImages;
		}
		public void setOutlineImages(boolean outlineImages) {
			this.outlineImages = outlineImages;
		}
		public Shape getShape(Vertex v) {
			Icon icon = (Icon) iconMap.get(v);

			if (icon != null && icon instanceof ImageIcon) {

				Image image = ((ImageIcon) icon).getImage();

				Shape shape = (Shape) shapeMap.get(image);
				if (shape == null) {
					if (shapeImages) {
						shape = FourPassImageShaper.getShape(image, 30);
					} else {
						shape = new Rectangle2D.Float(0, 0, 
								image.getWidth(null), image.getHeight(null));
					}
					if(shape.getBounds().getWidth() > 0 && 
							shape.getBounds().getHeight() > 0) {
						int width = image.getWidth(null);
						int height = image.getHeight(null);
						AffineTransform transform = 
							AffineTransform.getTranslateInstance(-width / 2, -height / 2);
						shape = transform.createTransformedShape(shape);
						shapeMap.put(image, shape);
					}
				}
				return shape;
			} else {
				return delegate.getShape(v);
			}
		}

		public Icon getIcon(ArchetypeVertex v) {
			if(fillImages) {
				return (Icon)iconMap.get(v);
			} else {
				return null;
			}
		}
	} //public static class DemoVertexImageShapeFunction


	/**
	 * a special renderer that can turn outlines on and off
	 * in this demo.
	 * You won't need this for a real application.
	 * Use PluggableRenderer instead
	 * 
	 * @author Tom Nelson - RABA Technologies
	 *
	 */
	class DemoRenderer extends PluggableRenderer {
		public void paintIconForVertex(Graphics g, Vertex v, int x, int y) {
			boolean outlineImages = false;
			if(vertexIconFunction instanceof DemoVertexImageShapeFunction) {
				outlineImages = ((DemoVertexImageShapeFunction)vertexIconFunction).isOutlineImages();
			}
			Icon icon = vertexIconFunction.getIcon(v);
			if(icon == null || outlineImages) {

				Shape s = AffineTransform.getTranslateInstance(x,y).
				createTransformedShape(getVertexShapeFunction().getShape(v));
				paintShapeForVertex((Graphics2D)g, v, s);

			}
			if(icon != null) {
				int xLoc = x - icon.getIconWidth()/2;
				int yLoc = y - icon.getIconHeight()/2;
				icon.paintIcon(screenDevice, g, xLoc, yLoc);
			}
		}
	} // class DemoRenderer

	/**
	 * a simple Icon that draws a checkmark in the lower-right quadrant of its
	 * area. Used to draw a checkmark on Picked Vertices.
	 */
	public static class Checkmark implements Icon {

		GeneralPath path = new GeneralPath();
		AffineTransform highlight = AffineTransform.getTranslateInstance(-1,-1);
		AffineTransform lowlight = AffineTransform.getTranslateInstance(1,1);
		AffineTransform shadow = AffineTransform.getTranslateInstance(2,2);
		Color color;
		public Checkmark() {
			this(Color.green);
		}
		public Checkmark(Color color) {
			this.color = color;
			path.moveTo(10,17);
			path.lineTo(13,20);
			path.lineTo(20,13);
		}
		public void paintIcon(Component c, Graphics g, int x, int y) {
			Shape shape = AffineTransform.getTranslateInstance(x, y).createTransformedShape(path);
			Graphics2D g2d = (Graphics2D)g;
			g2d.addRenderingHints(Collections.singletonMap(RenderingHints.KEY_ANTIALIASING, 
					RenderingHints.VALUE_ANTIALIAS_ON));
			g2d.setStroke(new BasicStroke(4));
			g2d.setColor(Color.darkGray);
			g2d.draw(shadow.createTransformedShape(shape));
			g2d.setColor(Color.black);
			g2d.draw(lowlight.createTransformedShape(shape));
			g2d.setColor(Color.white);
			g2d.draw(highlight.createTransformedShape(shape));
			g2d.setColor(color);
			g2d.draw(shape);
		}

		public int getIconWidth() {
			return 20;
		}

		public int getIconHeight() {
			return 20;
		}
	} // public static class Checkmark
	/**
	 * An icon that is made up of a collection of Icons.
	 * They are rendered in layers starting with the first
	 * Icon added (from the constructor).
	 * 
	 * @author Tom Nelson - RABA Technologies
	 *
	 */
	public static class LayeredIcon extends ImageIcon {

		Set iconSet = new LinkedHashSet();

		public LayeredIcon(Image image) {
			super(image);
		}

		public void paintIcon(Component c, Graphics g, int x, int y) {
			super.paintIcon(c, g, x, y);
			Dimension d = new Dimension(getIconWidth(), getIconHeight());
			for (Iterator iterator = iconSet.iterator(); iterator.hasNext();) {
				Icon icon = (Icon) iterator.next();
				Dimension id = new Dimension(icon.getIconWidth(), icon.getIconHeight());
				int dx = (d.width - id.width)/2;
				int dy = (d.height - id.height)/2;
				icon.paintIcon(c, g, x+dx, y+dy);
			}
		}

		public void add(Icon icon) {
			iconSet.add(icon);
		}

		public boolean remove(Icon icon) {
			return iconSet.remove(icon);
		}
	} // public static class LayeredIcon

//	/**
//	* a driver for this demo
//	*/
//	public static void main(String[] args) {
//	JFrame frame = new JFrame();
//	Container content = frame.getContentPane();
//	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

//	content.add(new VertexImageShaper(50));
//	frame.pack();
//	frame.setVisible(true);
//	}
} // public class VertexImageShaper
